//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Windows;
using OpenLS.Core.Serialization;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Internals;
using OpenLS.Spreadsheet.Parsing;
using System.Text;
using Expression = OpenLS.Spreadsheet.Expressions.Expression;

namespace OpenLS.Spreadsheet
{
    ///<summary>
    /// Represents a defined name of a workbook or worksheet. A defined name binds a name to a formula
    ///</summary>
    public class DefinedName : IOfficeXmlSerializable
    {
        internal DefinedName()
        {
        }

        internal DefinedName(Workbook workbook)
        {
            if (workbook == null)
                throw new ArgumentNullException("workbook");
            _workbook = workbook;
            Name = Enumerable.First(getAllNames(), n => Enumerable.All(_workbook.DefinedNames,
                                                                       dn => string.Compare(dn.Name, n, true) != 0));

        }
        protected static IEnumerable<string> getAllNames()
        {
            yield return "Name";
            for (int i = 1; i < 10000; i++)
                yield return "Name" + i; //\\loc;
        }
        internal DefinedName (Workbook workbook, string name)
            : this(workbook)
        {
            if (!isValidName(name))
                throw new ArgumentException("name");
            if (Enumerable.Any(_workbook.DefinedNames, dn => string.Compare(dn.Name,name, true)== 0))
                throw new ArgumentException("name");
            Name = name;
        }
        #region IOfficeXmlSerializable Members

        void IOfficeXmlSerializable.ReadXml(ReadContext context)
        {
            using (ReadContext c = context.Read("definedName"))
            {
                Name = c.GetString("name");
                Comment = c.GetOptionalString("comment");
                _customMenu = c.GetOptionalString("customMenu");
                _description = c.GetOptionalString("description");
                _help = c.GetOptionalString("help");
                _statusBar = c.GetOptionalString("statusBar");
                int? localSheetId = c.GetOptionalInteger("localSheetId");
                if (localSheetId != null)
                {
                    var man = context.GetData<InvalidSheetManager>();
                    var workbook = context.GetData<Workbook>();
                    LocalSheet = workbook.Sheets[man.GetValidIndex(localSheetId.Value)];
                }
                _hidden = c.GetOptionalBoolean("hidden", false);
                _function = c.GetOptionalBoolean("function", false);
                _xlm = c.GetOptionalBoolean("xlm", false);
                _functionGroupId = c.GetOptionalInteger("functionGroupId");
                _shortcutKey = c.GetOptionalString("shortcutKey");
                _publishToServer = c.GetOptionalBoolean("publishToServer", false);
                _workbookParameter = c.GetOptionalBoolean("workbookParameter", false);
                Formula= c.ReadString();
            }
        }

        void IOfficeXmlSerializable.WriteXml(WriteContext context)
        {
            if (string.IsNullOrEmpty(Formula))
                return;
            using (WriteContext c = context.Write("definedName"))
            {
                c.SetString("name", Name);
                c.SetOptionalString("comment", Comment);
                c.SetOptionalString("customMenu", _customMenu);
                c.SetOptionalString("description", _description);
                c.SetOptionalString("help", _help);
                c.SetOptionalString("statusBar", _statusBar);
                if (LocalSheet != null)
                {
                    c.SetInteger("localSheetId", LocalSheet.Workbook.Sheets.IndexOf(LocalSheet));
                }
                c.SetOptionalBoolean("hidden", _hidden, false);
                c.SetOptionalBoolean("function", _function,false);
                c.SetOptionalBoolean("xlm", _xlm , false);
                c.SetOptionalInteger("functionGroupId", _functionGroupId);
                c.SetOptionalString("shortcutKey", _shortcutKey);
                c.SetOptionalBoolean("publishToServer", _publishToServer, false);
                c.SetOptionalBoolean("workbookParameter", _workbookParameter, false);
                c.WriteString(Formula);
            }
        }

        #endregion

        private Expression _expression;
        internal Expression Expression
        {
            get
            {
                return _expression;
            }
            set
            {
                _expression = value;
                _valueSet = false;

            }
        }

        ///<summary>
        /// Get or set the formula of the defined name
        ///</summary>
        public string Formula
        {
            get
            {
                if (_expression == null)
                    return null;
                var textContext = new TextContext(new WorkbookEvaluationContext(_workbook));
                return _expression.GetText(textContext);
            }
            set
            {
                using (_workbook == null ? null : _workbook.CreateUndoScope(StringTable.DefineName))
                {
                    if (_workbook != null)
                        _workbook.AddUndoAction(Formula, f=> Formula=f);
                    if (value == null)
                    {
                        Expression = null;
                        return;
                    }
                    value = value.Trim();
                    if (!value.StartsWith("="))
                        value = "=" + value;
                    var p = new ExpressionParser(_workbook, LocalizationMode.NonLocalized, CellReferenceMode.Dynamic);
                    Expression = p.Expression(value);
                    _valueSet = false;
                }
            }
        }
        private object _value;
        private bool _valueSet;
        ///<summary>
        /// Get the actual value of the defined name
        ///</summary>
        public object Value
        {
            get
            {
                if (!_valueSet)
                {
                    _value = _expression == null ? null : _expression.GetObject(new WorkbookEvaluationContext(_workbook));
                    _valueSet = true;
                }
                return _value;
            }
            
        }

        private string _name;
        ///<summary>
        /// Get or set the name of the defined name. 
        ///</summary>
        ///<exception cref="ArgumentException"></exception>
        public string Name
        {
            get { return _name; }
            set
            {
                if (!isValidName(value))
                    // throw new ArgumentException();
                    MessageBox.Show("Invalid name");
                _name = value;
            }
        }

        protected static bool isValidName (string name)
        {
            if (string.IsNullOrEmpty(name))
                return false;
            foreach (char c in name)
            {
                if (!char.IsLetterOrDigit(c) && c != '_' && c != '.' && c != '?' && c != '\\' & c != '/') //\\ revent
                    return BuiltInDefinedNames.IsBuiltInName(name);
            }
            return true;

        }
        ///<summary>
        /// Get or set the comment of the defined name.
        ///</summary>
        public string Comment { get; set; }

        private string _customMenu;

        ///<summary>
        /// Get or set the custom menu text of the defined name.
        ///</summary>
        public string CustomMenu
        {
            get { return _customMenu; }
            set { _customMenu = value; }
        }

        private string _description;

        ///<summary>
        /// Get or set the description.
        ///</summary>
        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }


        private string _help;

        ///<summary>
        /// Get or set the help text 
        ///</summary>
        public string Help
        {
            get { return _help; }
            set { _help = value; }
        }

        private string _statusBar;

        public string StatusBar
        {
            get { return _statusBar; }
            set { _statusBar = value; }
        }

        public Sheet LocalSheet { get; set; }

        private bool _hidden;

        public bool Hidden
        {
            get { return _hidden; }
            set { _hidden = value; }
        }

        private bool _function;

        public bool Function
        {
            get { return _function; }
            set { _function = value; }
        }

        private bool _vbProcedure;

        public bool VbProcedure
        {
            get { return _vbProcedure; }
            set { _vbProcedure = value; }
        }

        private bool _xlm;

        public bool Xlm
        {
            get { return _xlm; }
            set { _xlm = value; }
        }

        private int? _functionGroupId;

        public int? FunctionGroupId
        {
            get { return _functionGroupId; }
            set { _functionGroupId = value; }
        }

        private string _shortcutKey;

        public string ShortcutKey
        {
            get { return _shortcutKey; }
            set { _shortcutKey = value; }
        }
        private bool _publishToServer;

        public bool PublishToServer
        {
            get { return _publishToServer; }
            set { _publishToServer = value; }
        }
        private bool _workbookParameter;

        public bool WorkbookParameter
        {
            get { return _workbookParameter; }
            set { _workbookParameter = value; }
        }




       
        internal bool Matches(Range range)
        {
            Range r = Value as Range;
            if (r != null)
                return r.Equals(range);
            return false;
        }
        internal bool Matches(Cell cell)
        {
            return Matches(new SingleCellRange(cell));
        }

        protected  Workbook _workbook;

        internal void ReadXml(ReadContext context)
        {
            IOfficeXmlSerializable s = this;
            s.ReadXml(context);
        }

        internal void Reset()
        {
            return; this._valueSet = false;
        }
    }
}
